/**
 * 表单编辑组件
 */
import { computed, h, ref, watch, type StyleValue } from 'vue'
import LowCodeItem from '../low-code/LowCodeItem'
import type { FormItemDto } from '../low-code/types'
import type { JSX } from 'vue/jsx-runtime'
import { isEmptyValue } from '@/utils/validate'
import type { AnyObject, IsObject } from '@/types'

import { isNotEmpty } from '@/utils/validators'
import { Button, Col, Form, FormItem, Modal, Row, type ColProps } from 'ant-design-vue'
import type { RuleObject } from 'ant-design-vue/es/form'

export type FormColProps = ColProps & {
    style?: StyleValue
}

export interface FormModalDto {
    show: boolean
    theme: 'dark' | 'light'
    id?: number | string
    title: string
    width?: number | string
    loading?: boolean
    labelCol?: FormColProps
    labelAlign?: 'left' | 'top' | 'right' | string // label对齐方式
    innerWidth?: string
    gutter?: number
    appendToBody?: boolean
    destroyOnClose?: boolean
    noPadding?: boolean
    params?: any
    items: {
        [key: string]: FormItemDto
    }
    rules?: {
        [key: string]: RuleObject[]
    }
    value?: any
    previewClass?: string // 回显table的class选项
    helps?: {
        [key: string]: (config: any) => void
    }
    init?: () => void
    complete: (data: any) => void
    cancel: () => void
}

export interface FormElDto {
    validate: () => Promise<void>;
    clearValidate: () => Promise<void>;
    clear: () => void;
    element: () => JSX.Element;
}

const formRef = ref()

const transform = (item: FormItemDto) => {
    return item.value
}

// 合并校验规则并批量添加数据转换方法
const formatRules = (data: FormModalDto) => {
    const rules = {} as { [key: string]: RuleObject[] }
    Object.keys(data.items).forEach((item: string) => {
        const itemRules = (data.items[item].rules || []).concat(data.rules?.[item] || []).concat(data.items[item].required ? [{
            required: true, validator: isNotEmpty, message: data.items[item].placeholder || `请填写${data.items[item].name}`, trigger: 'blur'
        }] : [])
        rules[item] = itemRules.map((rule: RuleObject) => {
            return {
                ...rule,
                transform: rule.transform || transform
            }
        })
    })
    return rules
}

// 渲染无弹窗的表单，需要手动获得validate方法用于校验
export function renderForm(data: FormModalDto) {
    const formRef = ref()
    const rules = computed(() => formatRules(data))
    // console.log(rules, 'rules')
    data.init && (data.init())
    return {
        validate: async () => {
            console.log(await formRef.value?.validate())
            formRef.value && await formRef.value.validate()
        },
        clearValidate: async () => {
            formRef.value && await formRef.value.clearValidate()
        },
        clear: () => {
            formRef.value && setTimeout(() => formRef.value.resetFields(), 100)
        },
        element: (key?: number) => {
            return <Form
                model={data.items}
                ref={formRef}
                rules={rules.value}
                label-col={data.labelCol || {
                    style: {
                        width: '6em'
                    }
                }}
                label-align={data.labelAlign || 'right'}
                scroll-to-error
                size={data?.params?.size}
                class={`lowcode-${data.theme}`}
                require-asterisk-position={'left'}
                key={key}
                onFinish={data.complete}
            >
                <Row gutter={data.gutter || 0}>
                    {Object.keys(data.items).map((item: string) => {
                        return data.items[item].hidden ? '' : <Col span={ (data.items[item].span || 24)}>
                            <FormItem
                                name={item}
                                label={data.items[item].name as string}
                                label-col={data.items[item]?.labelCol || undefined}
                                style={{
                                    width: data.items[item].width || data.innerWidth || 'auto'
                                }}
                                class={`${data.theme === 'dark' ? 'bg-white-dark-200' : '' }  rounded-sm ${data.noPadding ? '' : 'px-16 py-4'}`}
                            >
                                <div class={`${data.items[item]?.colClass || ''} w-full`}>
                                    {data.items[item].prefix && data.items[item].prefix}
                                    {LowCodeItem(data.items[item])}
                                    {data.items[item].suffix && data.items[item].suffix}
                                </div>
                            </FormItem>
                        </Col>
                    })}
                </Row>
                <button type="submit" class="hidden"></button>
            </Form>
        }
    } as FormElDto
}

// 渲染弹窗，内置了提交、取消和表单验证等处理
export default function renderModal(data: FormModalDto) {
    const form = renderForm(data)

    // watch(() => data.show, async () => {
    //     formRef.value && setTimeout(() => formRef.value.clearValidate(), 100)
    // })
    
    const onClick = async () => {
        await form.validate()
        data.loading = true
        try {
            await data.complete(getModelValue(data))
            data.loading = false
        } catch(err) {
            console.log('error', err)
            data.loading = false
        }
    }
    const onCancel = () => {
        data.cancel()
        form.clear()
        data.show = false
    }

    return <Modal
        v-model:open={data.show}
        title={data.title}
        width={ data.width || '520px' }
        onCancel={() => data.cancel()}
        append-to-body={!!data.appendToBody}
        destroy-on-close={!!data.destroyOnClose}
        v-slots={{
            footer: () => <span class="dialog-footer flex justify-center">
                <Button class="!h-40" onClick={onCancel}>{ data?.params?.concelText ?? '取消'}</Button>
                <Button
                    class="!h-40 !ml-24"
                    type="primary"
                    loading={data.loading}
                    disabled={data.loading}
                    onClick={onClick}
                >{ data?.params?.confirmText ?? '保存'}</Button>
            </span>
        }}
    >
        <div class="pt-24">
            {form.element()}
        </div>
    </Modal>
}

// // 渲染弹窗，内置了提交、取消和表单验证等处理
// export default function renderModal(data: FormModalDto) {

//     const rules = computed(() => formatRules(data))

//     watch(() => data.show, async () => {
//         formRef.value && setTimeout(() => formRef.value.clearValidate(), 100)
//     })
//     const onClick = async () => {
//         formRef.value && await formRef.value.validate()
//         data.loading = true
//         try {
//             await data.complete(getModelValue(data))
//             data.loading = false
//         } catch(err) {
//             console.log('error', err)
//             data.loading = false
//         }
//     }
//     data.init && (data.init())
//     return <Modal
//         v-model:open={data.show}
//         title={data.title}
//         width={ data.width || '520px' }
//         onCancel={() => data.cancel()}
//         append-to-body={!!data.appendToBody}
//         destroy-on-close={!!data.destroyOnClose}
//         v-slots={{
//             footer: () => <span class="dialog-footer flex justify-center">
//                 <Button class="!h-40" onClick={data.cancel}>{ data?.params?.concelText ?? '取消'}</Button>
//                 <Button
//                     class="!h-40 !ml-24"
//                     type="primary"
//                     loading={data.loading}
//                     disabled={data.loading}
//                     onClick={onClick}
//                 >{ data?.params?.confirmText ?? '保存'}</Button>
//             </span>
//         }}
//     >
//         <div class="border-t mb-24"></div>
//         <Form
//             class={`lowcode-${data.theme} remove-error-border`}
//             model={data.items}
//             ref={formRef}
//             rules={rules.value}
//             label-col={data.labelCol || 'auto'}
//             label-align={data.labelAlign || 'right'}
//             require-asterisk-position={'right'}
//             scroll-to-error
//             size={data?.params?.size}
//         >
//             <Row gutter={data.gutter || 0}>
//                 {Object.keys(data.items).map((item: string) => {
//                     return data.items[item].hidden ? '' : <Col span={(data.items[item].span || 24)}>
//                         {/* <div>
//                             {data.items[item].required && <span style="color: red;" class="mr-2px">*</span>}
//                             {data.items[item].name}
//                         </div> */}
//                         <FormItem
//                             class={`${data.theme === 'dark' ? 'bg-white-dark-200' : '' } rounded-sm ${data.noPadding ? '' : 'px-16 py-4'}`}
//                             name={item}
//                             label={data.items[item].name as string}
//                             label-col={data.items[item]?.labelCol || undefined}
//                             style={{
//                                 width: data.items[item].width || data.innerWidth || 'auto'
//                             }}
//                         >
//                             <div class={`${data.items[item].colClass ?? 'w-full'}`}>
//                                 {data.items[item].prefix && data.items[item].prefix}
//                                 {LowCodeItem(data.items[item])}
//                                 {data.items[item].suffix && data.items[item].suffix}
//                             </div>
//                         </FormItem>
//                     </Col>
//                 })}
//             </Row>
//         </Form>
//     </Modal>
// }

// 根据数据初始化表单数据
export function initModelValue(data: FormModalDto, payload?: any, resetHidden?: boolean): FormModalDto {
    Object.keys(data.items).forEach((itemKey: string) => {
        if (payload && !isEmptyValue(payload[itemKey])) {
            data.items[itemKey].value = payload[itemKey]
        } else {
            const defaultValue = typeof (data.items[itemKey]['defaultValue']) === 'function' ?
                (data.items[itemKey]['defaultValue'] as Function)() :
                data.items[itemKey]['defaultValue']
            data.items[itemKey].value = defaultValue ? JSON.parse(JSON.stringify(defaultValue)) : ''
        }
    })
    if (resetHidden) {
        initModelHidden(data)
    }
    return data
}

export function initModelHidden(data: FormModalDto, payload?: any): FormModalDto {
    Object.keys(data.items).forEach((itemKey: string) => {
        if(payload && !isEmptyValue(payload[itemKey])) {
            data.items[itemKey].hidden = payload[itemKey].defaultHidden ?? false
        } else {
            data.items[itemKey].hidden = data.items[itemKey].defaultHidden ?? data.items[itemKey].hidden ?? false
        }
    })
    
    return data
}

// 根据配置取出表单数据
export function getModelValue(data: FormModalDto, hideHidden: boolean = false) {
    const result: any = {}
    Object.keys(data.items).forEach((itemKey: string) => {
        if (!hideHidden || !data.items[itemKey].hidden) {
            result[itemKey] = data.items[itemKey].value ?? ''
        }
    })
    return result
}

// 禁用表单
export function disableModel(data: FormModalDto, exclude?: string[]) {
    Object.keys(data.items).forEach((itemKey: string) => {
        data.items[itemKey].readonly = !(exclude && exclude.includes(itemKey))
    })
    return data
}

// 取消表单禁用
export function editModel(data: FormModalDto, exclude?: string[]) {
    Object.keys(data.items).forEach((itemKey: string) => {
        data.items[itemKey].readonly = !!(exclude && exclude.includes(itemKey))
    })
    return data
}

// 提供便利方法，让回调函数的this指向当前组件
export function bindThis(data: {
    [key: string]: FormItemDto
}, that: AnyObject) {
    // 对所有的回调函数进行重新绑定this指向
    Object.keys(data).map(key => {
        type Callback = typeof callbacks
        type KeyType = keyof IsObject<Callback>
        const callbacks = data[key].callback
        Object.keys(callbacks || {}).map(callbackKey => {
            const obj = callbacks as IsObject<Callback>
            const key = callbackKey as KeyType
            const callback = obj[key]
            obj[key] = callback.bind(that)
        })
    })
    return data
}